/** Injects trackball events on incoming TCP connections.
 * 
 * @file inject.c
 * @author Kevin Lowe
 * @date December 12, 2010
 *
 * This program is compiled to run natively on android. It will run on
 * both the emulator and the Nexus One, however the IS_NEXUS_ONE define
 * must be changed to move between them.
 * 
 * Once running it will wait for an incoming TCP connection from a 
 * client. When the connection is recieved, it will inject a trackball
 * "move right" event into linux kernel. AFterwards, the connection will be
 * immediately closed without reading the actual contents of the message.
 */

#define IS_NEXUS_ONE 0 // 0 = Emulator, 1 = Nexus One
 
/** Included Libraries */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <linux/input.h>
#include <errno.h>
#include <unistd.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>

/** Global Variables */
static int deviceHandle = -1; // Trackball device handle

/** Function Declarations */
void OpenTrackballDevice();
void MoveTrackballRight();
int InjectEvent(int type, int code, int value);
void HandleClientConnection(int sock);

/** The main method is the entry point of the program.
 * It sets up the trackball device and TCP server. 
 * It waits for an accept connections from TCP clients. 
 */
int main()
{
	// Open the trackball device for injecting events
	OpenTrackballDevice();
	
	// Initiate TCP server variables
	int serversock, clientsock;
	struct sockaddr_in echoserver, echoclient;

	// Create the TCP socket
	if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
	{
		printf("Failed to create socket");
		exit(0);
	}
	
	memset(&echoserver, 0, sizeof(echoserver));       // Clear struct
	echoserver.sin_family = AF_INET;                  // Internet/IP
	echoserver.sin_addr.s_addr = htonl(INADDR_ANY);   // Allow any incoming address
	echoserver.sin_port = htons(2345);       		  // Server port
	
	// Bind the socket to the incoming server port.
	if (bind(serversock, (struct sockaddr *) &echoserver,
							   sizeof(echoserver)) < 0)
	{
		printf("Failed to bind");
		exit(0);
	}
	
	// Start listening to the socket.
	if (listen(serversock, 5) < 0)
	{
		printf("Failed to listen");
		exit(0);
	}
	
	// Wait for and process incoming connections in an infinite loop.
	while (1)
	{
		unsigned int clientlen = sizeof(echoclient);
		
		// Wait for a connection from a client.
		// accept() is a blocking function, so this program will stop exectuing until
		// an interupt is generated by the incoming client
		if ((clientsock = accept(serversock, (struct sockaddr *) &echoclient, &clientlen)) < 0)
		{
			printf("Failed to accept connect.");
			exit(0);
		}
		
		// The client has connected. Print the client IP address to the console.
		printf("Client connected: %s\n", inet_ntoa(echoclient.sin_addr));
		
		// Handle the connection to the client in another function.
		HandleClientConnection(clientsock);
	}
}

/** Opens the trackball device for injecting events. The location
 * of the trackball device is different on each device. 
 *
 * Note: "dev" is a folder at the root of the linux file system.
 *		 "dev" means "device".
 */
void OpenTrackballDevice()
{
	// Create the device string.
#if IS_NEXUS_ONE
	char device_str[100] = "/dev/input/event6";
#else
	char device_str[100] = "/dev/input/event0";
#endif
 
	// Attempt to open the device.
	if ((deviceHandle = open(device_str, O_RDWR)) == -1)
	{
		printf("cannot open device %s\n", device_str);
		exit(0);
	}
}

/** Injects an event into the opened device
 * This is not specific to the trackball and can be used for any device.
 */
int InjectEvent(int type, int code, int value)
{
	// Setup the input_event structure
    struct input_event ev;
    memset(&ev, 0, sizeof(ev));
	
	// Set the time of the event
    gettimeofday(&ev.time,0);
	
	// Set the event type, code and value
    ev.type = type;
    ev.code = code;
    ev.value = value;
	
	// Write the event to the device
    if (write(deviceHandle, &ev, sizeof(ev)) < 0)
    {
        printf("write event failed, %s\n", strerror(errno));
		return 0;
    }
	return 1;
}

/** Injects a trackball right event. This will be done differently
 * on each device. 
 */
void MoveTrackballRight()
{
	printf("Attempting to move trackball right.\n");

#if IS_NEXUS_ONE

	// Using the record program on the Nexus One it seems
	// we needed to inject 2 to move one incriment with the trackball.
	// 
	// Code: Left Right = 0, Up Down = 1
	// Value: Left Up = -2, Right Down = 2
	// Followed by 0,0,0 event.
	
	InjectEvent(EV_REL, 0, 2);
	InjectEvent(EV_SYN, 0, 0);
	
#else	

	// Using the record program on the emulator it seems
	// we needed to inject 4 to move one incriment with the trackball.
	// 
	// Code: Left Right = 0, Up Down = 1
	// Value: Left Up = -4, Right Down = 4
	// Followed by 0,0,0 event.
	
	InjectEvent(EV_REL, 0, 4);
	InjectEvent(EV_SYN, 0, 0);
	
#endif
	
}

/** Handles the connection from the client. It sends a trackball right
 * movement and immediately closes the connection without reading 
 * any messages.
 */
void HandleClientConnection(int sock)
{
	/* Execute Trackball Right no matter what the message is */
	MoveTrackballRight();
	
	/* Close the connection without reading any messages */
	close(sock);
}



